package middleware.redis;

import org.junit.Before;
import org.junit.Test;
import org.redisson.Redisson;
import org.redisson.api.*;
import org.redisson.api.BatchOptions.ExecutionMode;
import org.redisson.client.codec.StringCodec;
import org.redisson.config.Config;

import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * Redis 客户端测试
 *
 * @author ME
 * @date 2021/3/19
 */
public class RedisTest {

    private static RedissonClient redisson;

    /**
     * 每个 @Test 方法之前都会调用 @Before
     */
    @Before
    public void before() {
        Config config = new Config();
        // 默认使用 jackson编码方式，所以 key 格式要特殊处理
        config.setCodec(new StringCodec());
        config.useSingleServer()
                // .setPassword("me")
                .setAddress("redis://127.0.0.1:6379");// 可以用"rediss://"来启用SSL连接

        // 得到redisson对象
        redisson = Redisson.create(config);
    }

    @Test
    public void keys() {
        System.out.println("key 操作:");
        RKeys keys = redisson.getKeys();

        long keysAmount = keys.count();
        Iterable<String> allKeys = keys.getKeys();
        Iterable<String> foundedKeys = keys.getKeysByPattern("my-*");
        String randomKey = keys.randomKey();
        foundedKeys.forEach(System.out::println);

        long numOfDeletedKeys = keys.delete("obj1", "obj2", "obj3");
        long deletedKeysAmount = keys.deleteByPattern("test?");
    }

    @Test
    public void string() {
        System.out.println("string:");
        RBucket<String> bucket = redisson.getBucket("my-string", StringCodec.INSTANCE);
        System.out.println(bucket.get());

        RBuckets buckets = redisson.getBuckets();
        // List<RBucket<Object>> foundBuckets = buckets.find("my-*");

        // buckets.get() 只支持 String 类型
        Map<String, Object> loadedBuckets = buckets.get("my-string", "my-set");
        System.out.println("set 类型: " + loadedBuckets.get("my-set"));
        System.out.println("string 类型: " + loadedBuckets.get("my-string") + "\n");

        RBucket<String> str = redisson.getBucket("str");
        System.out.println("str: " + str.get());
        System.out.println(str.compareAndSet(null, "hhh"));
        System.out.println("str: " + str.get());
        System.out.println(str.compareAndSet(null, "value2"));
        System.out.println("str: " + str.get());
        System.out.println("del: " + str.delete() + "\n");

        System.out.println(str.setIfExists("if exists"));
    }

    @Test
    public void list() {
        System.out.println("list:");
        RList<String> list = redisson.getList("my-list");
        System.out.println(list);
    }

    @Test
    public void set() {
        System.out.println("set:");
        RSet<String> set = redisson.getSet("my-set");
        System.out.println(set);
    }

    @Test
    public void sortedSet() {
        System.out.println("SortedSet:");
        RSortedSet<String> sortedSet = redisson.getSortedSet("sset");
        // sortedSet.trySetComparator(new Comparator<String>() {
        //     @Override
        //     public int compare(String o1, String o2) {
        //         // 测试不生效？？？？？？？
        //         System.out.println(o1.compareTo(o2));
        //         return -(o1.compareTo(o2));
        //     }
        // });

        sortedSet.add("3");
        sortedSet.add("1");
        sortedSet.add("2");
        System.out.println(sortedSet);

        sortedSet.delete();
    }

    @Test
    public void scoredSortedSet() {
        System.out.println("SortedSet:");
        RScoredSortedSet<String> set = redisson.getScoredSortedSet("zset");
        set.add(10, "1yyy");
        set.add(20, "2yyy");
        set.add(30, "3yyy");

        for (String string : set) {
            // iteration through bulk loaded values
        }

        Map<String, Double> newValues = new HashMap<>();
        newValues.put("4yyy", 40D);
        newValues.put("5yyy", 50D);
        newValues.put("6yyy", 60D);
        int newValuesAmount = set.addAll(newValues);

        Double scoreResult = set.addScore("2yyy", 100);
        set.contains("4yyy");
        set.containsAll(Arrays.asList("3yyy", "4yyy", "5yyy"));

        String firstValue = set.first();
        // String polledFirst = set.pollFirst();
        // String polledLast = set.pollLast();

        // use read method to fetch all objects
        Collection<String> allValues = set.readAll();

        set.delete();
    }

    @Test
    public void map() {
        System.out.println("map:");
        RMap<String, String> map = redisson.getMap("my-hash");
        map.entrySet().stream().forEach(entry -> {
            System.out.println(entry.getKey() + "\t:\t" + entry.getValue());
        });
    }

    @Test
    public void script() {
        // https://gitee.com/uahioacnao/redisson/wikis/10.-%E9%A2%9D%E5%A4%96%E5%8A%9F%E8%83%BD#106-%E8%84%9A%E6%9C%AC%E6%89%A7%E8%A1%8C
        System.out.println("script:");
        String result = redisson.getScript().eval(RScript.Mode.READ_ONLY,
                "return redis.call('get', 'my-string')", RScript.ReturnType.VALUE);
        System.out.println(result);
    }

    @Test
    public void pipeline() {
        BatchOptions options = BatchOptions.defaults()
                // 指定执行模式
                // ExecutionMode.REDIS_READ_ATOMIC - 所有命令缓存在Redis节点中，以原子性事务的方式执行。
                // ExecutionMode.REDIS_WRITE_ATOMIC - 所有命令缓存在Redis节点中，以原子性事务的方式执行。
                // ExecutionMode.IN_MEMORY - 所有命令缓存在Redisson本机内存中统一发送，但逐一执行（非事务）。默认模式。
                // ExecutionMode.IN_MEMORY_ATOMIC - 所有命令缓存在Redisson本机内存中统一发送，并以原子性事务的方式执行。
                .executionMode(ExecutionMode.IN_MEMORY)
                // 告知Redis不用返回结果（可以减少网络用量）
                .skipResult()
                // 将写入操作同步到从节点
                // 同步到2个从节点，等待时间为1秒钟
                .syncSlaves(2, 1, TimeUnit.SECONDS)
                // 处理结果超时为2秒钟
                .responseTimeout(2, TimeUnit.SECONDS)
                // 命令重试等待间隔时间为2秒钟
                .retryInterval(2, TimeUnit.SECONDS)
                // 命令重试次数。仅适用于未发送成功的命令
                .retryAttempts(4);

        RBatch batch = redisson.createBatch();
        batch.getMap("test").fastPutAsync("1", "2222");
        batch.getMap("test").fastPutAsync("2", "9999");
        RFuture<Object> test = batch.getMap("test").putAsync("2", "5555");
        batch.getAtomicLong("counter").incrementAndGetAsync();
        batch.getAtomicLong("counter").incrementAndGetAsync();

        test.thenAccept(result -> {
            System.out.println("旧值为：" + result);
        }).exceptionally(throwable -> {
            System.out.println("异常为：" + throwable.getMessage());
            return null;
        });

        BatchResult<?> res = batch.execute();
        res.getSyncedSlaves();
        System.out.println(res.getResponses());

        redisson.getMap("test").delete();
        redisson.getAtomicLong("counter").delete();
    }

}
